package app.server.project.main.service.service;

import app.server.project.core.constants.Def;
import app.server.project.core.http.CMSBasePage;
import app.server.project.core.http.CMSLstResp;
import app.server.project.main.service.bo.event.AccountIPEvent;
import app.server.project.main.service.db.dao.user.AccountDao;
import app.server.project.main.service.db.dao.user.UserIpDao;
import app.server.project.main.service.db.entity.user.AccountEntity;
import app.server.project.main.service.db.entity.user.UserIPEntity;
import app.server.project.main.service.db.mapper.user.AccountMapper;
import app.server.project.main.service.dto.IPDto;
import app.server.project.main.service.dto.IPReq;
import app.server.project.main.service.dto.cms.IpCountDto;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;

@RequiredArgsConstructor
@Service
@Slf4j
public class IPService {

    private final static long QTime = 2 * 1000;
    private final static int SIZE = 20;
    private final static int MAX = 80;
    private final static String URL = "http://ip-api.com/batch?lang=zh-CN";

    private final LockService lockService;
    private final AccountDao accountDao;
    private final UserIpDao userIpDao;
    private final AccountMapper accountMapper;

    private final ConcurrentLinkedDeque<AccountIPEvent> eventQueue = new ConcurrentLinkedDeque<>();


    @EventListener(AccountIPEvent.class)
    public void onAccountIPEvent(AccountIPEvent event) {
        eventQueue.add(event);
    }

    public CMSLstResp<UserIPEntity> getUserIP(long userId) {
        var lock = this.lockService.getLock(userId);
        var ips = new ArrayList<UserIPEntity>();
        synchronized (lock) {
            ips.addAll(this.userIpDao.getAll(userId, SIZE));
        }
        return CMSLstResp.of(ips, ips.size());
    }

    public CMSLstResp<IpCountDto> getIpCount(CMSBasePage req) {
        Page<IpCountDto> page = Page.of(req.getPageIndex() + 1, req.getPageSize());
        var db = this.accountMapper.getAllByCountry(page);
        return CMSLstResp.of(db.getRecords(), db.getTotal());
    }

    public CMSLstResp<IpCountDto> getAllPayerCountry(CMSBasePage req) {
        Page<IpCountDto> page = Page.of(req.getPageIndex() + 1, req.getPageSize());
        var db = this.accountMapper.getAllPayerCountry(page);
        return CMSLstResp.of(db.getRecords(), db.getTotal());
    }

    @Scheduled(fixedDelay = QTime)
    public void consume() {
        List<IPReq> ipReqs = new ArrayList<>();
        int max = MAX;
        while (max > Def.Zero) {
            var event = this.eventQueue.pollFirst();
            if (event == null) {
                break;
            }
            ipReqs.add(IPReq.of(event.getIp(), event.getUserId(), event.getLoginTime(), "zh-CN"));
            max--;
        }
        if (ipReqs.isEmpty()) {
            return;
        }
        var respStr = HttpUtil.post(URL, JSONUtil.toJsonStr(ipReqs));

        if (JSONUtil.isTypeJSONObject(respStr)) {

            IPDto ret = JSONUtil.toBean(respStr, IPDto.class);
            if (!ret.isSuccess()) {
                return;
            }
            var event = ipReqs.stream().filter(v -> v.getQuery().equals(ret.getQuery())).findFirst().orElse(null);
            if (event == null) {
                return;
            }
            var lock = this.lockService.getLock(event.getUserId());
            synchronized (lock) {
                AccountEntity accountEntity = this.accountDao.getOpenIdByUserid(event.getUserId());
                accountEntity.setUserId(event.getUserId());
                accountEntity.setSynDb(true);
                if (StrUtil.isBlank(accountEntity.getIp())) {
                    accountEntity.setIp(ret.getQuery());
                    accountEntity.setCountry(ret.getCountry());
                    accountEntity.setLbs(respStr);
                }
                var ips = this.userIpDao.getAll(event.getUserId(), SIZE);
                UserIPEntity userIp;
                if (SIZE > ips.size()) {
                    userIp = UserIPEntity.of(event.getUserId(), IdUtil.getSnowflakeNextId());
                    userIp.setIp(ret.getQuery());
                    userIp.setCountry(ret.getCountry());
                    userIp.setLbs(respStr);
                    userIp.setLoginTime(event.getLoginTime());
                } else {
                    userIp = ips.get(ips.size() - 1);
                    userIp.setIp(event.getQuery());
                    userIp.setCountry(ret.getCountry());
                    userIp.setLbs(respStr);
                    userIp.setLoginTime(event.getLoginTime());
                    ips.remove(ips.size() - 1);
                }
                ips.add(Def.Zero, userIp);
            }
        }
        if (JSONUtil.isTypeJSONArray(respStr)) {
            var array = JSONUtil.parseArray(respStr);
            List<IPDto> respLst = array.toList(IPDto.class);
            for (IPDto resp : respLst) {
                if (!resp.isSuccess()) {
                    continue;
                }
                var event = ipReqs.stream().filter(v -> v.getQuery().equals(resp.getQuery())).findFirst().orElse(null);
                if (event == null) {
                    continue;
                }
                var lock = this.lockService.getLock(event.getUserId());
                synchronized (lock) {
                    AccountEntity accountEntity = this.accountDao.getOpenIdByUserid(event.getUserId());
                    accountEntity.setUserId(event.getUserId());
                    accountEntity.setSynDb(true);
                    if (StrUtil.isBlank(accountEntity.getIp())) {
                        accountEntity.setIp(resp.getQuery());
                        accountEntity.setCountry(resp.getCountry());
                        accountEntity.setLbs(JSONUtil.toJsonStr(resp));
                    }
                    var ips = this.userIpDao.getAll(event.getUserId(), SIZE);
                    UserIPEntity userIp;
                    if (SIZE > ips.size()) {
                        userIp = UserIPEntity.of(event.getUserId(), IdUtil.getSnowflakeNextId());
                        userIp.setIp(resp.getQuery());
                        userIp.setCountry(resp.getCountry());
                        userIp.setLbs(JSONUtil.toJsonStr(resp));
                        userIp.setLoginTime(event.getLoginTime());
                    } else {
                        userIp = ips.get(ips.size() - 1);
                        userIp.setIp(event.getQuery());
                        userIp.setCountry(resp.getCountry());
                        userIp.setLbs(JSONUtil.toJsonStr(resp));
                        userIp.setLoginTime(event.getLoginTime());
                        ips.remove(ips.size() - 1);
                    }
                    ips.add(Def.Zero, userIp);
                }


            }
        }
    }
}